home *** CD-ROM | disk | FTP | other *** search
/ MacFormat 1995 March / macformat-022.iso / Shareware City / Developers / GNU Diff Sources / GNU DIFF 1.15b Sources / diff.c~ < prev    next >
Encoding:
Text File  |  1992-04-13  |  19.9 KB  |  785 lines  |  [TEXT/ALFA]

  1. /* GNU DIFF main routine.
  2.    Copyright (C) 1988, 1989 Free Software Foundation, Inc.
  3.  
  4. This file is part of GNU DIFF.
  5.  
  6. GNU DIFF is free software; you can redistribute it and/or modify
  7. it under the terms of the GNU General Public License as published by
  8. the Free Software Foundation; either version 1, or (at your option)
  9. any later version.
  10.  
  11. GNU DIFF is distributed in the hope that it will be useful,
  12. but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14. GNU General Public License for more details.
  15.  
  16. You should have received a copy of the GNU General Public License
  17. along with GNU DIFF; see the file COPYING.  If not, write to
  18. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  19.  
  20. /* GNU DIFF was written by Mike Haertel, David Hayes,
  21.    Richard Stallman and Len Tower.  */
  22.  
  23. #define GDIFF_MAIN
  24. #include "diff.h"
  25. #include "regex.h"
  26. #include "getopt.h"
  27.  
  28.  
  29. /* Nonzero for -r: if comparing two directories,
  30.    compare their common subdirectories recursively.  */
  31.  
  32. int recursive;
  33.  
  34. /* For debugging: don't do discard_confusing_lines.  */
  35.  
  36. int no_discards;
  37.  
  38. /* Return a string containing the command options with which diff was invoked.
  39.    Spaces appear between what were separate ARGV-elements.
  40.    There is a space at the beginning but none at the end.
  41.    If there were no options, the result is an empty string.
  42.  
  43.    Arguments: OPTIONVEC, a vector containing separate ARGV-elements, and COUNT,
  44.    the length of that vector.  */
  45.  
  46. void    usage(void);
  47.  
  48. static char *
  49. option_list (optionvec, count)
  50.      char **optionvec;  /* Was `vector', but that collides on Alliant.  */
  51.      int count;
  52. {
  53.   int i;
  54.   int length = 0;
  55.   char *result;
  56.  
  57.   for (i = 0; i < count; i++)
  58.     length += strlen (optionvec[i]) + 1;
  59.  
  60.     result = (char *) xmalloc ((size_t)length + 1);
  61.   result[0] = 0;
  62.  
  63.   for (i = 0; i < count; i++)
  64.     {
  65.       strcat (result, " ");
  66.       strcat (result, optionvec[i]);
  67.     }
  68.  
  69.   return result;
  70. }
  71.  
  72. /* The numbers 129 and 130 that appear in the fourth element
  73.    for the context and unidiff entries are used as a way of
  74.    telling the big switch in `main' how to process those options.  */
  75.  
  76. static struct option longopts[] =
  77. {
  78.   {"ignore-blank-lines", 0, 0, 'B'},
  79.   {"context", 2, 0, 129},
  80.   {"ifdef", 1, 0, 'D'},
  81.   {"show-function-line", 1, 0, 'F'},
  82.   {"speed-large-files", 0, 0, 'H'},
  83.   {"ignore-matching-lines", 1, 0, 'I'},
  84.   {"file-label", 1, 0, 'L'},
  85.   {"entire-new-files", 0, 0, 'N'},
  86.   {"new-files", 0, 0, 'N'},
  87.   {"starting-file", 1, 0, 'S'},
  88.   {"initial-tab", 0, 0, 'T'},
  89.   {"text", 0, 0, 'a'},
  90.   {"all-text", 0, 0, 'a'},
  91.   {"ascii", 0, 0, 'a'},
  92.   {"ignore-space-change", 0, 0, 'b'},
  93.   {"minimal", 0, 0, 'd'},
  94.   {"ed", 0, 0, 'e'},
  95.   {"reversed-ed", 0, 0, 'f'},
  96.   {"ignore-case", 0, 0, 'i'},
  97.   {"print", 0, 0, 'l'},
  98.   {"rcs", 0, 0, 'n'},
  99.   {"show-c-function", 0, 0, 'p'},
  100.   {"binary", 0, 0, 'q'},
  101.   {"brief", 0, 0, 'q'},
  102.   {"recursive", 0, 0, 'r'},
  103.   {"report-identical-files", 0, 0, 's'},
  104.   {"expand-tabs", 0, 0, 't'},
  105.   {"ignore-all-space", 0, 0, 'w'},
  106.   {"unified", 2, 0, 130},
  107.   {"version", 0, 0, 'v'},
  108.   {0, 0, 0, 0}
  109. };
  110.  
  111. #ifdef    THINK_C
  112. char    *form_path(char *, char *);
  113.  
  114. char *
  115. form_path (s1, s2)
  116.     char *s1, *s2;
  117.     {
  118.     char    *temp = s1;
  119.     int    colon_flag = 0;
  120.     char    *retval;
  121.     
  122.     if ( ! strchr (s1, ':') )
  123.     {
  124.         temp = concat (":", s1, "");
  125.         colon_flag = 1;
  126.     }
  127.     retval = concat (temp, ":", s2);
  128.     if ( colon_flag )
  129.         free (temp);
  130.     return (retval);
  131.     }
  132. #else
  133. #define    form_path(dir, name)        concat((dir), "/", (name))
  134. #endif    THINK_C
  135.  
  136. #ifdef    THINK_C
  137. int main_diff2 (argc, argv)
  138. #else
  139. int main (argc, argv)
  140. #endif
  141.      int argc;
  142.      char *argv[];
  143. {
  144.   int val;
  145.   int c;
  146.   int prev = -1;
  147.   int longind;
  148.   extern char *version_string;
  149.  
  150.   program = argv[0];
  151.  
  152.   /* Do our initializations. */
  153.   output_style = OUTPUT_NORMAL;
  154.   always_text_flag = FALSE;
  155.   ignore_space_change_flag = FALSE;
  156.   ignore_all_space_flag = FALSE;
  157.   length_varies = FALSE;
  158.   ignore_case_flag = FALSE;
  159.   ignore_blank_lines_flag = FALSE;
  160.   ignore_regexp = 0;
  161.   function_regexp = 0;
  162.   print_file_same_flag = FALSE;
  163.   entire_new_file_flag = FALSE;
  164.   no_details_flag = FALSE;
  165.   context = -1;
  166.     line_end_char = NEWLINE;
  167.   tab_align_flag = FALSE;
  168.   tab_expand_flag = FALSE;
  169.   recursive = FALSE;
  170.   paginate_flag = FALSE;
  171.   ifdef_string = NULL;
  172.   heuristic = FALSE;
  173.   dir_start_file = NULL;
  174.   msg_chain = NULL;
  175.   msg_chain_end = NULL;
  176.   no_discards = 0;
  177.  
  178.   /* Decode the options.  */
  179.  
  180.   while ((c = getopt_long (argc, argv,
  181.                "0123456789abBcC:dD:efF:hHiI:lL:nNpqrsS:tTuvw",
  182.                longopts, &longind)) != EOF)
  183.     {
  184.       if (c == 0)        /* Long option. */
  185.     c = longopts[longind].val;
  186.     switch (c)
  187.       {
  188.     /* All digits combine in decimal to specify the context-size.  */
  189.       case '1':
  190.       case '2':
  191.       case '3':
  192.       case '4':
  193.       case '5':
  194.       case '6':
  195.       case '7':
  196.       case '8':
  197.       case '9':
  198.       case '0':
  199.     if (context == -1)
  200.       context = 0;
  201.       /* If a context length has already been specified,
  202.          more digits allowed only if they follow right after the others.
  203.          Reject two separate runs of digits, or digits after -C.  */
  204.       else if (prev < '0' || prev > '9')
  205.         fatal ("context length specified twice");
  206.  
  207.       context = context * 10 + c - '0';
  208.     break;
  209.  
  210.       case 'a':
  211.     /* Treat all files as text files; never treat as binary.  */
  212.     always_text_flag = 1;
  213.     break;
  214.  
  215.       case 'b':
  216.     /* Ignore changes in amount of whitespace.  */
  217.     ignore_space_change_flag = 1;
  218.     length_varies = 1;
  219.     break;
  220.  
  221.       case 'B':
  222.     /* Ignore changes affecting only blank lines.  */
  223.     ignore_blank_lines_flag = 1;
  224.     break;
  225.  
  226.     case 'C':
  227.     case 129:        /* +context[=lines] */
  228.     case 130:        /* +unified[=lines] */
  229.       if (optarg)
  230.         {
  231.           if (context >= 0)
  232.         fatal ("context length specified twice");
  233.           {
  234.         char *p;
  235.         for (p = optarg; *p; p++)
  236.           if (*p < '0' || *p > '9')
  237.             fatal ("invalid context length argument");
  238.           }
  239.           context = atoi (optarg);
  240.         }
  241.  
  242.       /* Falls through.  */
  243.       case 'c':
  244.     /* Make context-style output.  */
  245.       specify_style (c == 130 ? OUTPUT_UNIFIED : OUTPUT_CONTEXT);
  246.     break;
  247.  
  248.       case 'd':
  249.     /* Don't discard lines.  This makes things slower (sometimes much
  250.        slower) but will find a guaranteed minimal set of changes.  */
  251.     no_discards = 1;
  252.     break;
  253.  
  254.     case 'D':
  255.       /* Make merged #ifdef output.  */
  256.       specify_style (OUTPUT_IFDEF);
  257.       ifdef_string = optarg;
  258.       break;
  259.  
  260.       case 'e':
  261.     /* Make output that is a valid `ed' script.  */
  262.     specify_style (OUTPUT_ED);
  263.     break;
  264.  
  265.       case 'f':
  266.     /* Make output that looks vaguely like an `ed' script
  267.        but has changes in the order they appear in the file.  */
  268.     specify_style (OUTPUT_FORWARD_ED);
  269.     break;
  270.  
  271.       case 'F':
  272.     /* Show, for each set of changes, the previous line that
  273.        matches the specified regexp.  Currently affects only
  274.        context-style output.  */
  275.     function_regexp = optarg;
  276.     break;
  277.  
  278.       case 'h':
  279.     /* Split the files into chunks of around 1500 lines
  280.        for faster processing.  Usually does not change the result.
  281.  
  282.        This currently has no effect.  */
  283.     break;
  284.  
  285.       case 'H':
  286.       /* Turn on heuristics that speed processing of large files
  287.          with a small density of changes.  */
  288.     heuristic = 1;
  289.     break;
  290.  
  291.       case 'i':
  292.     /* Ignore changes in case.  */
  293.     ignore_case_flag = 1;
  294.     break;
  295.  
  296.       case 'I':
  297.     /* Ignore changes affecting only lines that match the
  298.        specified regexp.  */
  299.     ignore_regexp = optarg;
  300.     break;
  301.  
  302.       case 'l':
  303.     /* Pass the output through `pr' to paginate it.  */
  304.     paginate_flag = 1;
  305.     break;
  306.  
  307.     case 'L':
  308.       /* Specify file labels for `-c' output headers.  */
  309.       if (!file_label[0])
  310.         file_label[0] = optarg;
  311.       else if (!file_label[1])
  312.         file_label[1] = optarg;
  313.       else
  314.         fatal ("too many file label options");
  315.       break;
  316.  
  317.       case 'n':
  318.     /* Output RCS-style diffs, like `-f' except that each command
  319.        specifies the number of lines affected.  */
  320.     specify_style (OUTPUT_RCS);
  321.     break;
  322.  
  323.       case 'N':
  324.     /* When comparing directories, if a file appears only in one
  325.        directory, treat it as present but empty in the other.  */
  326.     entire_new_file_flag = 1;
  327.     break;
  328.  
  329.     case 'p':
  330.       /* Make context-style output and show name of last C function.  */
  331.       specify_style (OUTPUT_CONTEXT);
  332.       function_regexp = "^[_a-zA-Z]";
  333.       break;
  334.  
  335.     case 'q':
  336.       no_details_flag = 1;
  337.       break;
  338.  
  339.       case 'r':
  340.     /* When comparing directories, 
  341.        recursively compare any subdirectories found.  */
  342.     recursive = 1;
  343.     break;
  344.  
  345.       case 's':
  346.     /* Print a message if the files are the same.  */
  347.     print_file_same_flag = 1;
  348.     break;
  349.  
  350.       case 'S':
  351.     /* When comparing directories, start with the specified
  352.        file name.  This is used for resuming an aborted comparison.  */
  353.     dir_start_file = optarg;
  354.     break;
  355.       
  356.       case 't':
  357.     /* Expand tabs to spaces in the output so that it preserves
  358.        the alignment of the input files.  */
  359.     tab_expand_flag = 1;
  360.     break;
  361.  
  362.       case 'T':
  363.     /* Use a tab in the output, rather than a space, before the
  364.        text of an input line, so as to keep the proper alignment
  365.        in the input line without changing the characters in it.  */
  366.     tab_align_flag = 1;
  367.     break;
  368.  
  369.     case 'v':
  370.       printf ("GNU diff version %s\n", version_string);
  371.       break;
  372.  
  373.     case 'u':
  374.       /* Output the context diff in unidiff format.  */
  375.       specify_style (OUTPUT_UNIFIED);
  376.       break;
  377.  
  378.       case 'w':
  379.     /* Ignore horizontal whitespace when comparing lines.  */
  380.     ignore_all_space_flag = 1;
  381.     length_varies = 1;
  382.     break;
  383.  
  384.     default:
  385.       usage ();
  386.       }
  387.       prev = c;
  388.     }
  389.  
  390.   if (optind != argc - 2)
  391.     usage ();
  392.  
  393.   if (ignore_regexp)
  394.     {
  395.       char *val;
  396.       bzero (&ignore_regexp_compiled, sizeof ignore_regexp_compiled);
  397.       val = re_compile_pattern (ignore_regexp, strlen (ignore_regexp),
  398.                 &ignore_regexp_compiled);
  399.       if (val != 0)
  400.             error ("%s: %s", ignore_regexp, val);
  401.         ignore_regexp_compiled.fastmap = (char *) xmalloc ((size_t)256);
  402.     }
  403.  
  404.   if (function_regexp)
  405.     {
  406.       char *val;
  407.       bzero (&function_regexp_compiled, sizeof function_regexp_compiled);
  408.       val = re_compile_pattern (function_regexp, strlen (function_regexp),
  409.                 &function_regexp_compiled);
  410.       if (val != 0)
  411.             error ("%s: %s", function_regexp, val);
  412.         function_regexp_compiled.fastmap = (char *) xmalloc ((size_t)256);
  413.     }
  414.  
  415.   if (output_style != OUTPUT_CONTEXT && output_style != OUTPUT_UNIFIED)
  416.     context = 0;
  417.   else if (context == -1)
  418.     /* Default amount of context for -c.  */
  419.     context = 3;
  420.  
  421.   switch_string = option_list (argv + 1, optind - 1);
  422.  
  423.     val = compare_files (NULL, argv[optind], NULL, argv[optind + 1], 0);
  424.  
  425.   /* Print any messages that were saved up for last.  */
  426.   print_message_queue ();
  427.  
  428.   if (ferror (stdout) || fclose (stdout) != 0)
  429.     fatal ("write error");
  430.   return (val);
  431. }
  432.  
  433. static    void
  434. usage ()
  435. {
  436.   fprintf (stderr, "\
  437. Usage: diff [-#] [-abBcdefhHilnNprstTuvw] [-C lines] [-F regexp] [-I regexp]\n\
  438.        [-L label [-L label]] [-S file] [-D symbol] [+ignore-blank-lines]\n\
  439.        [+context[=lines]] [+unified[=lines]] [+ifdef=symbol]\n\
  440.        [+show-function-line=regexp]\n");
  441.   fprintf (stderr, "\
  442.        [+speed-large-files] [+ignore-matching-lines=regexp] [+new-file]\n\
  443.        [+initial-tab] [+starting-file=file] [+text] [+all-text] [+ascii]\n\
  444.        [+minimal] [+ignore-space-change] [+ed] [+reversed-ed] [+ignore-case]\n");
  445.   fprintf (stderr, "\
  446.        [+print] [+rcs] [+show-c-function] [+binary] [+brief] [+recursive]\n\
  447.        [+report-identical-files] [+expand-tabs] [+ignore-all-space]\n\
  448.        [+file-label=label [+file-label=label]] [+version] path1 path2\n");
  449.   exit (2);
  450.  
  451. specify_style (style)
  452.      enum output_style style;
  453. {
  454.   if (output_style != OUTPUT_NORMAL
  455.       && output_style != style)
  456.     error ("conflicting specifications of output style");
  457.   output_style = style;
  458. }
  459.  
  460. static char Standard_Input[] = "Standard Input";
  461.  
  462. /*
  463.  * open_file_pair:
  464.  *
  465.  * Opens two files for input and gets their Stat (directory info) entries.  The routine
  466.  * returns 0 if the open was successful and it has not yet been determined
  467.  * whether they are different.  It returns 1 if the files are exactly
  468.  * the same.  It returns 2 if there was an error opening one of the files.
  469.  *
  470.  * If "always_open" is set, then the files are always opened (provided there is no error),
  471.  * even if it is determined that the files are the same.  In this case, 1 is never returned
  472.  * by open_file_pair.
  473.  *
  474.  * It is the caller's responsibility to close the files and free the memory to store the
  475.  * file name (Yuck!).  Free "inf[X].name" only if it is not NULL and the value for "dirX" passed
  476.  * was not NULL.  Close "inf[X].desc" if it is >= 0.  How confusing!
  477.  */
  478. int
  479. open_file_pair (struct file_data *inf, char *dir0, char *name0, char *dir1, char *name1)
  480.     {
  481.     int stat_result[2];
  482.     int    errorcount = 0;
  483.     int    i;
  484.     
  485.   /* Mark any nonexistent file with -1 in the desc field.  */
  486.   /* Mark unopened files (i.e. directories) with -2. */
  487.  
  488.   inf[0].desc = name0 == 0 ? -1 : -2;
  489.   inf[1].desc = name1 == 0 ? -1 : -2;
  490.  
  491.   /* Now record the full name of each file, including nonexistent ones.  */
  492.  
  493.   if (name0 == 0)
  494.     name0 = name1;
  495.   if (name1 == 0)
  496.     name1 = name0;
  497.  
  498.     inf[0].name = dir0 == 0 ? name0 : form_path (dir0, name0);
  499.     inf[1].name = dir1 == 0 ? name1 : form_path (dir1, name1);
  500.  
  501.   /* Stat the files.  Record whether they are directories.
  502.      Record in stat_result whether stat fails.  */
  503.  
  504.     for (i = 0; i <= 1; i++)
  505.     {
  506.       bzero (&inf[i].stat, sizeof(struct stat));
  507.         inf[i].dir_p = 0;
  508.         stat_result[i] = 0;
  509.  
  510.         if (inf[i].desc != -1)
  511.         {
  512.             char *filename = inf[i].name;
  513.  
  514.       stat_result[i] = 
  515.         strcmp (filename, "-")
  516.           ? stat (filename, &inf[i].stat)
  517.           : fstat (0, &inf[i].stat);
  518.           
  519.             if (stat_result[i] < 0)
  520.             {
  521.                 perror_with_name (filename);
  522.                 errorcount = 1;
  523.             }
  524.             else
  525.                 inf[i].dir_p = 
  526.                    S_IFDIR == (inf[i].stat.st_mode & S_IFMT)
  527.           && strcmp (filename, "-");
  528.         }
  529.     }
  530. #if !defined(MSDOS) && !defined(THINK_C)
  531.     /*
  532.     ** this stuff is real bad idea under MSDOS, at least for MSC 5.1
  533.     ** because the st_ino and st_dev fields are not supported by
  534.     ** MSDOS, and so stat sets them to zero; therefore
  535.     ** this test always succeeds.
  536.     */
  537.  
  538.     /* See if the two named files are actually the same physical file.
  539.       If so, we know they are identical without actually reading them.  */
  540.  
  541.     if (output_style != OUTPUT_IFDEF
  542.       && inf[0].stat.st_ino == inf[1].stat.st_ino
  543.         && inf[0].stat.st_dev == inf[1].stat.st_dev
  544.         && stat_result[0] == 0
  545.         && stat_result[1] == 0)
  546.     {
  547.         return 1;    /* The files are exactly the same. */
  548.     }
  549. #endif    /* !defined(MSDOS) && !defined(THINK_C) */
  550.   if (name0 == 0)
  551.     inf[0].dir_p = inf[1].dir_p;
  552.   if (name1 == 0)
  553.     inf[1].dir_p = inf[0].dir_p;
  554.  
  555.     /* Open the files and record their descriptors.  */
  556.  
  557.     for (i = 0; i <= 1; i++)
  558.     {
  559.         if (inf[i].desc == -1)
  560.             ;
  561.         else if (!strcmp (inf[i].name, "-"))
  562.         {
  563.             inf[i].desc = 0;
  564.             inf[i].name = Standard_Input;
  565.         }
  566.         /* Don't bother opening if stat already failed.  */
  567.         else if (stat_result[i] == 0 && ! inf[i].dir_p)
  568.         {
  569.             char *filename = inf[i].name;
  570.  
  571.             inf[i].desc = open (filename, O_RDONLY);
  572.             if (0 > inf[i].desc)
  573.             {
  574.                 perror_with_name (filename);
  575.                 errorcount = 1;
  576.             }
  577.         }
  578.     }
  579.     if ( errorcount )
  580.         return (2);    /* There was an error opening one of the files. */
  581.     return (0);    /* The two files may be different, but it is not yet known. */
  582.     }
  583.  
  584. /* Compare two files (or dirs) with specified names
  585.    DIR0/NAME0 and DIR1/NAME1, at level DEPTH in directory recursion.
  586.    (if DIR0 is 0, then the name is just NAME0, etc.)
  587.    This is self-contained; it opens the files and closes them.
  588.  
  589.    Value is 0 if files are identical, 1 if different,
  590.    2 if there is a problem opening them.  */
  591.  
  592. int
  593. compare_files (dir0, name0, dir1, name1, depth)
  594.      char *dir0, *dir1;
  595.      char *name0, *name1;
  596.      int depth;
  597. {
  598.   struct file_data inf[2];
  599.   int val;
  600.   int errorcount = 0;
  601.  
  602.     bzero (&inf[0], sizeof(inf[0]));
  603.     bzero (&inf[1], sizeof(inf[1]));
  604.     
  605.   /* If this is directory comparison, perhaps we have a file
  606.      that exists only in one of the directories.
  607.      If so, just print a message to that effect.  */
  608.  
  609.   if (! entire_new_file_flag && (name0 == 0 || name1 == 0))
  610.     {
  611.       char *name = name0 == 0 ? name1 : name0;
  612.       char *dir = name0 == 0 ? dir1 : dir0;
  613.       message ("Only in %s: %s\n", dir, name);
  614.       /* Return 1 so that diff_dirs will return 1 ("some files differ").  */
  615.       return 1;
  616.     }
  617.  
  618.     errorcount = open_file_pair (&inf[0], dir0, name0, dir1, name1);
  619.  
  620.     if ( errorcount == 1 )
  621.     {
  622.         /* The files are exactly the same. */
  623.         val = 0;
  624.         goto done;
  625.     }
  626.  
  627.   if (errorcount)    /* If non-zero, it must be 2, meaning there was a file error. */
  628.     {
  629.  
  630.       /* If either file should exist but fails to be opened, return 2.  */
  631.  
  632.       val = 2;
  633.  
  634.     }
  635.     else if (inf[0].dir_p && inf[1].dir_p)
  636.     {
  637.       if (output_style == OUTPUT_IFDEF)
  638.     fatal ("-D option not supported with directories");
  639.  
  640.       /* If both are directories, compare the files in them.  */
  641.  
  642.       if (depth > 0 && !recursive)
  643.     {
  644.       /* But don't compare dir contents one level down
  645.          unless -r was specified.  */
  646.             message ("Common subdirectories: %s and %s\n",
  647.             inf[0].name, inf[1].name);
  648.       val = 0;
  649.     }
  650.       else
  651.     {
  652.             /* This is almost redundant; by closing sooner, we
  653.               avoid any danger of running out of descriptors
  654.               on deep recursion.  */
  655.             if ( inf[0].desc >= 0 )
  656.             {
  657.                 close (inf[0].desc);
  658.                 inf[0].desc = -1;
  659.             }
  660.             if ( inf[1].desc >= 0 )
  661.             {
  662.                 close (inf[1].desc);
  663.                 inf[1].desc = -1;
  664.             }
  665.             val = diff_dirs (inf[0].name, inf[1].name,
  666.                     compare_files, depth, 0, 0);
  667.         }
  668.  
  669.     }
  670.     else if (depth == 0 && (inf[0].dir_p || inf[1].dir_p))
  671.     {
  672.  
  673.       /* If only one is a directory, and it was specified in the command line,
  674.      use the file in that dir whose basename matches the other file.  */
  675.  
  676.         int dir_arg = (inf[0].dir_p ? 0 : 1);
  677.         int fnm_arg = (inf[0].dir_p ? 1 : 0);
  678. #ifdef    THINK_C
  679.       char *p = rindex (inf[fnm_arg].name, ':');
  680. #else
  681.       char *p = rindex (inf[fnm_arg].name, '/');
  682. #endif
  683.         char *filename = form_path (inf[dir_arg].name, (p ? p+1 : inf[fnm_arg].name));
  684.  
  685.       if (inf[fnm_arg].name == Standard_Input)
  686.     fatal ("can't compare - to a directory");
  687.  
  688.         inf[dir_arg].desc = open (filename, O_RDONLY);
  689.  
  690.         if (0 > inf[dir_arg].desc)
  691.     {
  692.       perror_with_name (filename);
  693.       val = 2;
  694.     }
  695.       else
  696.     {
  697.       /* JF: patch from the net to check and make sure we can really free
  698.          this.  If it's from argv[], freeing it is a *really* bad idea */
  699.       if (0 != (dir_arg ? dir1 : dir0))
  700.             free (inf[dir_arg].name);
  701.             inf[dir_arg].name = filename;
  702.             if (fstat (inf[dir_arg].desc, &inf[dir_arg].stat) < 0)
  703.                 pfatal_with_name (inf[dir_arg].name);
  704.  
  705.             inf[dir_arg].dir_p
  706.                 = (S_IFDIR == (inf[dir_arg].stat.st_mode & S_IFMT));
  707.             if (inf[dir_arg].dir_p)
  708.         {
  709.           error ("%s is a directory but %s is not",
  710.                 inf[dir_arg].name, inf[fnm_arg].name);
  711.                 val = 1;
  712.         }
  713.       else
  714.                 val = diff_2_files (inf, depth);
  715.     }
  716.  
  717.     }
  718.     else if (depth > 0 && (inf[0].dir_p || inf[1].dir_p))
  719.     {
  720.       /* Perhaps we have a subdirectory that exists only in one directory.
  721.      If so, just print a message to that effect.  */
  722.  
  723.         if (inf[0].desc == -1 || inf[1].desc == -1)
  724.     {
  725.       if (entire_new_file_flag && recursive)
  726.         val = diff_dirs (inf[0].name, inf[1].name, compare_files, depth,
  727.                  inf[0].desc == -1, inf[1].desc == -1);
  728.       else
  729.         {
  730.           char *dir = (inf[0].desc == -1) ? dir1 : dir0;
  731.       message ("Only in %s: %s\n", dir, name0);
  732.           val = 1;
  733.         }
  734.     }
  735.       else
  736.     {
  737.       /* We have a subdirectory in one directory
  738.          and a file in the other.  */
  739.  
  740.             if (inf[0].dir_p)
  741.         message ("%s is a directory but %s is not\n",
  742.                     inf[0].name, inf[1].name);
  743.       else
  744.         message ("%s is a directory but %s is not\n",
  745.                     inf[1].name, inf[0].name);
  746.       /* This is a difference.  */
  747.       val = 1;
  748.     }
  749.     }
  750.   else
  751.     {
  752.  
  753.       /* Both exist and both are ordinary files.  */
  754.  
  755.         val = diff_2_files (inf, depth);
  756.  
  757.     }
  758.  
  759.   /* Now the comparison has been done, if no error prevented it,
  760.      and VAL is the value this function will return.  */
  761.  
  762.     if (inf[0].desc >= 0)
  763.         close (inf[0].desc);
  764.     if (inf[1].desc >= 0)
  765.         close (inf[1].desc);
  766.  
  767.  done:
  768.   if (val == 0 && !inf[0].dir_p)
  769.     {
  770.       if (print_file_same_flag)
  771.     message ("Files %s and %s are identical\n",
  772.             inf[0].name, inf[1].name);
  773.     }
  774.   else
  775.   fflush (stdout);
  776.  
  777.   if (dir0 != 0)
  778.         free (inf[0].name);
  779.   if (dir1 != 0)
  780.         free (inf[1].name);
  781.  
  782.   return val;
  783. }
  784.